class SintaksnoStablo:

    def __init__(self):
        self.cvorovi = []

    def dodaj_korijen(self):
        korijen = Cvor(self, '<prijevodna_jedinica>')
        self.cvorovi.append(korijen)
        return(korijen)

    def deklarirano_globalno(self, ime, tip):
        """Provjerava postoji li globalna deklaracija, ako postoji vraca
        referencu na unos u tablici znakova, inace vraca 0"""
        for i in self.cvorovi[0].djeca:
            for j in i.tablica_znakova:
                if j.ime == ime and j.tip == [tip] and j.je_def == 0:
                    return(j)
        return(0)

    def ispisi(self):
        for n in self.cvorovi:
            n.ispisi(0)


class Cvor:

    def __init__(self, _roditelj, _ime, _redak='', _ljedinka=''):
        self.roditelj = _roditelj
        self.ime = _ime
        self.redak = _redak
        self.ljedinka = _ljedinka

        # svaki cvor ima svaki moguci tip :P
        # npr za tip niz(const(X)) tip=['X'], const=['1'], niz=['1']
        self.djeca = []
        self.tablica_znakova = []
        self.tip = []
        self.imena = []
        self.br_elem = ''
        self.l_izraz = []
        self.ime_funkcije = ''
        self.param = []
        self.pov = []
        self.niz = []
        self.const = []
        self.ntip = []
        self.br_elem = []

    def dodaj_cvor(self, ime, redak='', ljedinka=''):

        if redak == '' and ljedinka == '':  # nezavrsni znak
            novi_cvor = Cvor(self, ime)
        else:
            novi_cvor = Cvor(self, ime, redak, ljedinka)

        self.djeca.append(novi_cvor)
        return(novi_cvor)

    def pronadi_deklaraciju(self, idn='', tip='', lokalno=0):
        """Pretrazuje tablicu znakova. Bez argumenta vraca cvor u koji se moze
        zabiljeziti deklaracija, sa argumentom <idn> vraca referencu na instancu
        klase Identifikator koja sadrzi identifikator <idn>. Ako lokalno != 0
        pretrazuje samo lokalni kontekst"""
        cvor = self

        while (cvor.ime != '<slozena_naredba>' and
               cvor.ime != '<vanjska_deklaracija>' and
               cvor.ime != '<prijevodna_jedinica>'):
            if isinstance(cvor.roditelj, SintaksnoStablo): # korijen
                return(0)
            cvor = cvor.roditelj

        if (idn != ''):
            for n in cvor.tablica_znakova: # varijable = [[ime, tip, l-izraz]]
                if n.ime == idn:
                    return(n)
            if lokalno == 0:
                return(cvor.roditelj.pronadi_deklaraciju(idn=idn)) # visa razina
            else:
                return(0)

        else:
            return(cvor)

    def dodaj_deklaraciju_varijable(self, idn):
        """U kontekst najblize (vise) razine dodaje instancu klase
        Identifikator"""
        if not isinstance(idn, Identifikator):
            raise TypeError("idn mora biti Identifikator")
        cvor = self
        if not self.unutar_funkcije():
            while not isinstance(cvor.roditelj, SintaksnoStablo):
                cvor = cvor.roditelj
        else:
            cvor = self.pronadi_deklaraciju()
        if cvor == 0:
            return(0)
        cvor.tablica_znakova.append(idn)
        return(1)

    def dodaj_deklaraciju_funkcije(self, idn):
        #deklaracija funkcije nalazi se u globalnom kontekstu
        if not isinstance(idn, Identifikator):
            raise TypeError("idn mora biti Identifikator")

        cvor = self
        while not isinstance(cvor.roditelj, SintaksnoStablo): #korijen stabla
            cvor = cvor.roditelj
        cvor.tablica_znakova.append(idn)

    def pronadi_deklaraciju_funkcije(self, idn):
        cvor = self

        while not isinstance(cvor.roditelj, SintaksnoStablo): #korijen stabla
            cvor = cvor.roditelj
        for n in cvor.tablica_znakova:
            if n.ime == idn and n.tip == ['funkcija']:
                return(n)
        return(0)

    def dodaj_parametre_funkcije(self):
        naredba = cvor.djeca[5] #<slozena_naredba>
        imena = cvor.djeca[3]   #<lista_parametara>

        for n in range(0, imena.imena):
            varijabla = Identifikator(imena.imena[n], tip=list(imena.tip[n]))
            naredba.dodaj_deklaraciju_varijable(Identifikator)

    def razrjesuje_u(self, ime):
        """vrijedi li cvor *=> ime?"""
        cvor = self
        if cvor.ime == ime:
            return(cvor)
        for n in range(0, len(cvor.djeca)):
            return(cvor.djeca[n].razrjesuje_u(ime))
        return(0)

    def unutar_funkcije(self):
        """Za return naredbu, vraca ime funkcije"""
        cvor = self

        while (not isinstance(cvor, SintaksnoStablo)) and cvor.ime != '<definicija_funkcije>':
            cvor = cvor.roditelj

        if isinstance(cvor, SintaksnoStablo):
            return(0)
        return(cvor.ime_funkcije)

    def ispisi(self, razina):
        if self.ime[0] == '<':
            print(" " * razina + self.ime, self.tip, self.ntip, self.ime_funkcije)
            if len(self.tablica_znakova) > 0:
                for n in self.tablica_znakova:
                    print(n.ime, n.tip, n.l_izraz)
        else:
            print(" " * razina + "%s %s %s" % (self.ime,
                                               self.redak,
                                               self.ljedinka))
        for n in self.djeca:
            n.ispisi(razina+1)

class Identifikator():
    """Unos u tablici znakova"""

    def __init__(self, ime, tip=[], params=[], pov=[], konst='',
                 l_izraz='', je_def=0):
        self.ime = ime
        if isinstance(tip, list): # tip mora biti lista
            self.tip = tip
        else:
            self.tip = [tip]
        self.params = params  # parametar funkcije ili tip elemenata niza
        if isinstance(pov, list): # pov mora biti lista
            self.pov = pov
        else:
            self.pov = [pov]
        self.konst = konst
        self.l_izraz = l_izraz
        self.je_def = je_def
